using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs.LowLevel.Unsafe;
using Unity.Mathematics;

namespace HTCollections.Unsafe
{
    public static class UnsafeUtil
    {
        /// <summary>
        /// 分配指定长度内存
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe T* Malloc<T>(int length, Allocator allocator = Allocator.Persistent)
            where T : unmanaged
        {
            int size = sizeof(T);
            int alignment = UnsafeUtility.AlignOf<T>();
            var ptr = UnsafeUtility.Malloc(length * size, alignment, allocator);
            return (T*)ptr;
        }

        /// <summary>
        /// 分配指定长度内存
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe T* Malloc<T>(int length, int size, Allocator allocator = Allocator.Persistent)
            where T : unmanaged
        {
            int alignment = UnsafeUtility.AlignOf<T>();
            var ptr = UnsafeUtility.Malloc(length * size, alignment, allocator);
            return (T*)ptr;
        }

        /// <summary>
        /// 释放内存
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void Free<T>(T* ptr, Allocator allocator = Allocator.Persistent)
            where T : unmanaged
        {
            UnsafeUtility.Free(ptr, allocator);
        }

        /// <summary>
        /// 清理内存
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void Clear<T>(T* ptr, long length)
            where T : unmanaged
        {
            int size = sizeof(T);
            UnsafeUtility.MemClear(ptr, length * size);
        }

        /// <summary>
        /// 内存复制
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void MemCpy<T>(T* src, T* dst, int length) where T : unmanaged
        {
            int size = sizeof(T);
            UnsafeUtility.MemCpy(dst, src, length * size);
        }

        /// <summary>
        /// 内存复制
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void MemCpy<T>(T* src, T* dst, int dstIndex, int length) where T : unmanaged
        {
            int size = sizeof(T);
            UnsafeUtility.MemCpy((byte*)dst + dstIndex * size, src, length * size);
        }

        /// <summary>
        /// 内存复制
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void MemCpy<T>(T* src, int srcIndex, T* dst, int dstIndex, int length)
            where T : unmanaged
        {
            int size = sizeof(T);
            UnsafeUtility.MemCpy((byte*)dst + dstIndex * size, (byte*)src + srcIndex * size, length * size);
        }

        /// <summary>
        /// 扩容数组
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe T* ResizeUnsafeArray<T>(T* array, int oldLength, int newLength, Allocator allocator)
            where T : unmanaged
        {
            T* newArray = Malloc<T>(newLength, allocator);
            MemCpy<T>(array, newArray, newLength > oldLength ? oldLength : newLength);
            Free(array, allocator);
            return newArray;
        }

        /// <summary>
        /// JobSystem 中分配内存
        /// </summary>
        /// <param name="t">分配器</param>
        /// <param name="sizeOf"></param>
        /// <param name="alignOf"></param>
        /// <param name="items"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static unsafe void* Allocate<T>(ref this T t, int sizeOf, int alignOf, int items)
            where T : unmanaged, AllocatorManager.IAllocator
        {
            return (void*)AllocateBlock(ref t, sizeOf, alignOf, items).Range.Pointer;
        }

        public static AllocatorManager.Block AllocateBlock<T>(ref this T t, int sizeOf, int alignOf, int items)
            where T : unmanaged, AllocatorManager.IAllocator
        {
            AllocatorManager.Block block = default;
            block.Range.Pointer = IntPtr.Zero;
            block.Range.Items = items;
            block.Range.Allocator = t.Handle;
            block.BytesPerItem = sizeOf;
            block.Alignment = math.max(JobsUtility.CacheLineSize, alignOf);
            var error = t.Try(ref block);
            return block;
        }

        public static unsafe void CopyArray<T>(
            void* src,
            int srcIndex,
            T[] dst,
            int dstIndex,
            int length)
            where T : unmanaged
        {
            if (dst == null)
                throw new ArgumentNullException(nameof(dst));

            GCHandle gcHandle = GCHandle.Alloc((object)dst, GCHandleType.Pinned);
            UnsafeUtility.MemCpy(
                (void*)((IntPtr)(void*)gcHandle.AddrOfPinnedObject() + dstIndex * UnsafeUtility.SizeOf<T>()),
                (void*)((IntPtr)src + srcIndex * UnsafeUtility.SizeOf<T>()),
                (long)(length * UnsafeUtility.SizeOf<T>()));
            gcHandle.Free();
        }
    }
}